package com.example.springboot.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.example.springboot.common.Constants;
import com.example.springboot.controller.request.BaseRequest;
import com.example.springboot.entity.Book;
import com.example.springboot.entity.Borrow;
import com.example.springboot.entity.Returnbook;
import com.example.springboot.entity.User;
import com.example.springboot.exception.ServiceException;
import com.example.springboot.mapper.BookMapper;
import com.example.springboot.mapper.BorrowMapper;
import com.example.springboot.mapper.UserMapper;
import com.example.springboot.mapper.po.BorrowReturnCountPO;
import com.example.springboot.service.IBorrowService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Description:
 *
 * @Author 马钰涵
 * @Create 2023/8/10 16:29
 * @Version 1.0
 */
@Slf4j
@Service
public class BorrowServiceImpl implements IBorrowService {
    @Autowired
    private BorrowMapper borrowMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private BookMapper bookMapper;


    @Override
    public List<Borrow> list() {
        return borrowMapper.list();
    }

    @Override
    public PageInfo<Borrow> page(BaseRequest baseRequest) {
        PageHelper.startPage(baseRequest.getPageNum(), baseRequest.getPageSize());
        List<Borrow> borrows = borrowMapper.listByCondition(baseRequest);
        for (Borrow borrow : borrows) {
            LocalDate returnDate = borrow.getReturnTime();
            LocalDate now = LocalDate.now();
            if (now.plusDays(1).isEqual(returnDate)) {  //当前日期比归还日期小一天
                borrow.setNote("即将到期");
            } else if (now.isEqual(returnDate)) {
                borrow.setNote("已到期");
            } else if (now.isAfter(returnDate)) {
                borrow.setNote("已过期");
            } else {
                borrow.setNote("正常");
            }
        }
        return new PageInfo<>(borrows);
    }

    @Override
    @Transactional  //同时成功或失败
    public void save(Borrow borrow) {
        //1.校验用户的积分是否足够
        String userNo = borrow.getUserNo();
        User user = userMapper.getByUsername(userNo);
        if (Objects.isNull(user)) {
            throw new ServiceException(Constants.USER_IS_NOT_EXITS_MSG);
        }
        //2.校验图书数量是否足够
        Book book = bookMapper.getByBookNo(borrow.getBookNo());
        if (Objects.isNull(book)) {
            throw new ServiceException(Constants.BOOK_IS_NOT_EXITS_MSG);
        }
        //3.校验图书数量
        if (book.getNumbers() < 1) {
            throw new ServiceException(Constants.BOOK_IS_NOT_ENOUGH_MSG);
        }
        Integer account = user.getAccount();
        Integer score = book.getScore() * borrow.getDays();        //score=用户借一本所用积分*天数
        //4.校验账余额
        if (account < score) {
            throw new ServiceException(Constants.SCORE_IS_NOT_ENOUGH_MSG);
        }
        //5.更新余额
        user.setAccount(user.getAccount() - score);
        userMapper.updateById(user);
        //6.更新图书数量
        book.setNumbers(book.getNumbers() - 1);
        bookMapper.updateById(book);

        borrow.setReturnTime(LocalDate.now().plus(borrow.getDays(), ChronoUnit.DAYS));//当前日期+days=归还日期
        borrow.setScore(score);
        //7.新增就借书记录
        borrowMapper.save(borrow);
    }

    @Override
    public PageInfo<Returnbook> pageReturnbook(BaseRequest baseRequest) {
        PageHelper.startPage(baseRequest.getPageNum(), baseRequest.getPageSize());
        return new PageInfo<>(borrowMapper.listReturnbookByCondition(baseRequest));
    }

    @Transactional
    @Override
    public void saveReturn(Returnbook returnbook) {
        //1.改状态
        returnbook.setStatus("已归还");
        borrowMapper.updateStatus("已归还", returnbook.getId());
        returnbook.setId(null);  //新数据
        //2.更新还书时间
        returnbook.setRealTime(LocalDate.now());
        borrowMapper.saveReturnbook(returnbook);
        //3.更新图书数量
        bookMapper.updateNumberByNo(returnbook.getBookNo());
        //4.返还和扣除积分
        Book book = bookMapper.getByBookNo(returnbook.getBookNo());
        if (book != null) {
            long until = 0;
            if (returnbook.getRealTime().isBefore(returnbook.getReturnTime())) {//规定期限类归还
                until = returnbook.getRealTime().until(returnbook.getReturnTime(), ChronoUnit.DAYS);//获取天数
            } else if (returnbook.getRealTime().isAfter(returnbook.getReturnTime())) {//逾期归还，扣积分
                until = -returnbook.getReturnTime().until(returnbook.getRealTime(), ChronoUnit.DAYS);
            }
            int score = (int) until * book.getScore();//获取带归还的积分
            User user = userMapper.getByUsername(returnbook.getUserNo());
            int account = user.getAccount() + score;
            user.setAccount(account);
            if (account < 0) {//锁定账号
                user.setStatus(false);
            }
            userMapper.updateById(user);
        }
    }

    @Override
    public Borrow getById(Integer id) {
        return borrowMapper.getById(id);
    }

    @Override
    public void update(Borrow borrow) {
        borrowMapper.updateById(borrow);
    }

    @Override
    public void deleteById(Integer id) {
        borrowMapper.deleteById(id);
    }

    @Override
    public void deleteReturnById(Integer id) {
        borrowMapper.deleteReturnById(id);
    }

    @Override
    public Map<String, Object> getCountByTimeRange(String timeRange) {
        Map<String, Object> map = new HashMap<>();
        Date today = new Date();
        List<DateTime> dateRange;
        switch (timeRange) {
            case "week":
                // offsetDay 计算时间的一个工具方法
                // rangeToList 返回从开始时间到结束时间的一个时间范围
                dateRange = DateUtil.rangeToList(DateUtil.offsetDay(today, -6), today, DateField.DAY_OF_WEEK);
                break;
            case "month":
                dateRange = DateUtil.rangeToList(DateUtil.offsetDay(today, -29), today, DateField.DAY_OF_MONTH);
                break;
            case "month2":
                dateRange = DateUtil.rangeToList(DateUtil.offsetDay(today, -59), today, DateField.DAY_OF_MONTH);
                break;
            case "month3":
                dateRange = DateUtil.rangeToList(DateUtil.offsetDay(today, -89), today, DateField.DAY_OF_MONTH);
                break;
            default:
                dateRange = new ArrayList<>();
        }
        //  datetimeToDateStr() 就是一个处理的方法， 把 DateTime类型的List转换成一个 String的List
        List<String> dateStrRange = datetimeToDateStr(dateRange);
        map.put("date", dateStrRange);  // x轴的日期数据生产完毕

        //  timeRange = week  month
        // getCountByTimeRange 不会统计数据库没有的日期，比如 数据库 11.4 这一天数据没有，他不会返回 date=2022-11-04,count=0 这个数据
        List<BorrowReturnCountPO> borrowCount = borrowMapper.getCountByTimeRange(timeRange, 1);   // 1 borrow  2 return
        List<BorrowReturnCountPO> returnCount = borrowMapper.getCountByTimeRange(timeRange, 2);
        map.put("borrow", countList(borrowCount, dateStrRange));
        map.put("returnbook", countList(returnCount, dateStrRange));
        return map;
    }

    @Override
    public void deleteBatch(List<Integer> ids) {
        borrowMapper.deleteBatch(ids);
    }

    @Override
    public void deleteReturnBatch(List<Integer> ids) {
        borrowMapper.deleteReturnBatch(ids);
    }

    // 把 DateTime类型的List转换成一个 String的List
    private List<String> datetimeToDateStr(List<DateTime> dateTimeList) {
        List<String> list = CollUtil.newArrayList();
        if (CollUtil.isEmpty(dateTimeList)) {
            return list;
        }
        for (DateTime dateTime : dateTimeList) {
            String date = DateUtil.formatDate(dateTime);
            list.add(date);
        }
        return list;
    }

//     对数据库未统计的时间进行处理
    private List<Integer> countList(List<BorrowReturnCountPO> countPOList, List<String> dateRange) {
        List<Integer> list = CollUtil.newArrayList();
        if (CollUtil.isEmpty(countPOList)) {
            return list;
        }
        for (String date : dateRange) {
            // .map(BorrowReturCountPO::getCount) 取出 对象里的 count值
            // orElse(0) 对没匹配的数据返回0
            // "2022-11-04" 没有的话 就返回0
            Integer count = countPOList.stream().filter(countPO -> date.equals(countPO.getDate()))
                    .map(BorrowReturnCountPO::getCount).findFirst().orElse(0);
            list.add(count);
        }
        // 最后返回的list的元素个数会跟 dateRange 的元素个数完全一样
        // dateRange: [
        //            "2022-10-30",
        //            "2022-10-31",
        //            "2022-11-01",
        //            "2022-11-02",
        //            "2022-11-03",
        //            "2022-11-04",
        //            "2022-11-05"
        //        ],
        // borrow: [
        //            0,
        //            0,
        //            2,
        //            1,
        //            0,
        //            1,
        //            3
        //        ]
        return list;
    }
}
